using System;
using System.Collections;
using System.Collections.Generic;

using Supremacy.Annotations;

using System.Linq;

namespace Supremacy.Collections
{
    public static class IndexedEnumerable
    {
        #region Dependent Type: EmptyIndexedCollection<T>
        private sealed class EmptyIndexedCollection<T> : IIndexedCollection<T>
        {
            private static EmptyIndexedCollection<T> _instance;

            internal static EmptyIndexedCollection<T> Instance
            {
                get
                {
                    if (_instance == null)
                        _instance = new EmptyIndexedCollection<T>();
                    return _instance;
                }
            }
            #region Implementation of IEnumerable
            public IEnumerator<T> GetEnumerator()
            {
                yield break;
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
            #endregion

            #region Implementation of IIndexedEnumerable<T>
            public int Count
            {
                get { return 0; }
            }

            public T this[int index]
            {
                get { throw new ArgumentOutOfRangeException("index"); }
            }
            #endregion

            #region Implementation of IIndexedCollection<T>
            public bool Contains(T value)
            {
                return false;
            }

            public int IndexOf(T value)
            {
                return -1;
            }
            #endregion
        }
        #endregion

        #region Dependent Type: IndexedEnumerableWrapper<T, TInternal>
        private sealed class IndexedEnumerableWrapper<T, TInternal> : IIndexedEnumerable<T>
        {
            private readonly IIndexedEnumerable<TInternal> _innerItems;
            private readonly Func<TInternal, T> _conversionCallback;

            public IndexedEnumerableWrapper(
                [NotNull] IIndexedEnumerable<TInternal> innerItems,
                [NotNull] Func<TInternal, T> conversionCallback)
            {
                if (innerItems == null)
                    throw new ArgumentNullException("innerItems");
                if (conversionCallback == null)
                    throw new ArgumentNullException("conversionCallback");
                _innerItems = innerItems;
                _conversionCallback = conversionCallback;
            }

            public IEnumerator<T> GetEnumerator()
            {
                return _innerItems.Select(o => _conversionCallback(o)).GetEnumerator();
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }

            public int Count
            {
                get { return _innerItems.Count; }
            }

            public T this[int index]
            {
                get { return _conversionCallback(_innerItems[index]); }
            }
        }
        #endregion


        public static IIndexedEnumerable<T> Empty<T>()
        {
            return EmptyIndexedCollection<T>.Instance;
        }

        public static IIndexedEnumerable<TDestination> Cast<TSource, TDestination>(this IIndexedEnumerable<TSource> source)
        {
            return new IndexedEnumerableWrapper<TDestination, TSource>(source, o => (TDestination)((object)o));
        }
    }
}